#ifndef GLENGINE_H
#define GLENGINE_H

/* Copyright (C) 2011 Riccardo Marson

   This file is part of OpenPool, an open-source game of pool. 

   Permission is hereby granted, free of charge, to any person obtaining a
   copy of this software and associated documentation files (the "Software"),
   to deal in the Software without restriction, including without limitation
   the rights to use, copy, modify, merge, publish, distribute, sublicense,
   and/or sell copies of the Software, and to permit persons to whom the
   Software is furnished to do so, subject to the following conditions:

   The above copyright notice and this permission notice shall be included
   in all copies or substantial portions of the Software.

   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
   OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
   RICCARDO MARSON BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
   AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
   CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

  See Readme.rtf for further information.
  
*/

#include "CMatrix4x4.h"

#include "enumerations.h"
#include "eventmanager.h"
#include "logmanager.h"

#include "globjects.h"


class CRenderWindow
{
    class CGraphicsListener : public CBaseEventListener
    {
    public:
        CGraphicsListener(string name, CRenderWindow *parent);
        virtual ~CGraphicsListener();

        virtual bool processEvent(EventPtr event);

    private:

        CRenderWindow *mParent;

    };

    EventListenerPtr mEventListener;

    friend class CRenderWindow::CGraphicsListener;

public:

    CRenderWindow(string listenerName = "GraphicsListener");
    ~CRenderWindow();

    //get window width
    int width();
    //get window height
    int height();
    // resize window
    void resize(unsigned int w, unsigned int h);
    //retrieve and manage window-related events
    void pollUIEvents();
    //passes the camera created in the main window to the GL context
    void initCamera(GLCameraPtr camera);
    //adds a light created externally to the scene.
    //Note: shadow-mapping only supports one light at the moment.
    void addLight(GLLightPtr light);
    //adds an object to the scene
    void addObject(GLObjectPtr obj);

    //loads a 3D .obj model file. for props and avatar models alike.
    const ModelOBJ* loadObjModel(const char* objFilename);

    //removes all balls from the scene.
    void clearScene();

    //updates the position of an avatar. Entity / avatar pairs are uniquely
    //identified by their ObjectD. Only balls need to be updated as we are sure the
    //other props are static.
    void updateAvatar(eObjectID ballID, CVector3 newPosition, CVector3 newHeading,
                      CVector3 newRotAxis, float newRotAngle);

    //forces a call to updateGL(). Used in the Main Loop to control graphics updates.
    void updateGraphics();

    //changes the state of castShadows. if true, shadows are generated.
    void toggleShadows(bool shadowOn);

    //toggles physics debug mode based on config options
    void setDebugModeActive(bool physicsDebugMode);

    //returns the current observer position.
    CVector3 getCurrentCameraPosition();
    //returns the current observer look point.
    CVector3 getCurrentCameraDirection();

protected:

    //re-implemented virtual functions

    //openGL related
    void initializeGL(); //created the openGL context
    void renderScene();  //paint the scene, autmatically called by this->update();
    void resizeGL(int width, int height); //adapts the GL viewport if the window is resized,
    //so that there are no deformations.

    //human interaction
    void mousePressEvent(sf::Mouse::Button button, int x, int y);
    void mouseMoveEvent(int x, int y);
    void mouseWheelEvent(int delta);
    void keyPressEvent(sf::Key::Code key);

private:

    //Utility functions used internally by the widget

    //zooms the camera in/out (moves the camera along the current viewing direction).
    void moveCamera(float dx);
    //rotates the camera around a sphere of constant radius. look point is always the center of the pool table.
    void rotateCamera(float dx, float dy);

    //draws a gived .obj 3d model, using OpenGL fixed pipeline. GLSL version is coming....
    void drawObjModelFixedPipeline(const ModelOBJ* model, bool texturesOn);

    //draws all the objects currently in the scene, using their defaults (3d models, textures, ...)
    void drawScene();

    //In this render mode, only shadow-casting objects are rendered, all geometries are un-textured and balls are
    //drawn using gluSphere instead of the 3d obj model. Used only during the first render pass of the shadow map
    //generation (i.e. when the depth buffer is saved in the shadow texture).
    void drawShadowCastingObjects();

    //Here only objects that can be reflected off the ground plane are drawn
    void drawReflectedObjects();

    //computes the matrix transofrmation needed to project the shadow texture
    //from "light space" to "camera space"
    void shadowMapProjection();
    //recalculates camera and light view and projection matrices. Only used in
    //shadow-mapping mode. During a standard render, this is replaced by a simple call to
    //gluLookAt()
    void updateWorldMatrices();

    //generated and computes the projection of the reflection texture
    void generateReflectionMap();

    //Shadow Mapping functions:

    /*This is the first shadow-mapping render pass. The scene is drawn from the
    light viewpoint and the contents of the depth buffer are saved in the shadowMap
    texture.*/
    void shadowPassDepth();
    //This is the second shadow-mapping render pass. The scene is drawn from the camera
    //view point, but the global illumination is dimmed down (i.e. draws the shadows).
    void shadowPassDim();
    /*This is the third and final shadow-mapping render pass. The scene is again drawn
    from the camera view point but this time the global illumination is set to normal
    levels. The shadow testure is projected to camera space. The depth of each pixel is
    evaluated against the shadow texture. If the comparison fails (i.e. the pixel is in shadow)
    the results from the previous render pass are allowed to show through.*/
    void shadowPassFinal();
    //just calls the three shadow-mapping passes in sequence.
    void shadowRender();

    //renders the scene without shadows.
    void standardRender();

    //Renders a small portion of the scene using OpenGL selection mode.
    //Used for picking 3d objects clicked by the mouse.
    void selectionRender(int mouseX, int mouseY);

    //saves a screenshot of the current screen
    void saveScreenshot();

    //each pool ball is uniquely identified by its ball id, both in the physics system and in the graphics.
    //using a map instead of a list is more efficient when trying to find a specific ball to update.
    typedef map< eObjectID, GLObjectPtr > PoolBalls;
    typedef map< string, GLObjectPtr > SceneProps;
    typedef map< string, const ModelOBJ* > ModelsMap;
    typedef map< string, GLuint > TexturesMap;

    //Member variables:

    sf::RenderWindow *mWindow;

    int mWidth;
    int mHeight;

    struct Point {
        int x;
        int y;
    };

    Point lastPos; //holds the last position clicked by the mouse.
    Point mousePivot; //stores the mouse screen coordinates when the mouse mode is switched to shooting.
    //then as long as shooting mode is active, the mouse position is reset to this point after each movement,
    //to allow for better / easier control of the cue rotation.
    GLCameraPtr mCamera;
    list<GLLightPtr> mLights; //holds the list of lights present in the scene.
    //currently only one. In standard render mode there could be more than one.
    //shadow-mapping mode does not support more than one light at the moment.

    PoolBalls mBalls; //the list of all balls present in the scene.

    SceneProps mProps; //the list of all props present in the scene (pool table, ground, chalk cube, ...).
    //A prop is a mesh present in the graphics which has no physics counterpart and cannot be moved or acted upon in any way.
    //usually a 3d model, can also be a simple mesh (e.g. the ground plane);

    // Holds unique and constant references to loaded obj models.
    // Can only be modified / unloaded through the cache.
    // For convenience, each objects with an associated model holds a constant reference to its ModelOBJ too.
    // however, since more objects might share the same model, it is necessary to keep track of the total number of
    // different resources loaded for the current scene.
    ModelsMap mModels;

    //holds OpenGL Texture IDs for all the textures loaded/created. Each texture is identified by name
    TexturesMap mTextures;

    int shadowMapWidth; //width of the shadow texture
    int shadowMapHeight; //height of the shadow texture
    int reflectionMapWidth; //width of the reflection texture
    int reflectionMapHeight; //height of the reflection texture

    //projected texture mapping bias matrix. initialized in initializeGL()
    CMatrix4x4 biasMatrix;

    //the following matrices are only used in shadow-mapping mode.
    CMatrix4x4 lightProjection; //Used to store the current ligth projection matrix
    CMatrix4x4 lightView; //Used to store the current ligth view matrix
    CMatrix4x4 cameraProjection; //Used to store the current camera projection matrix
    CMatrix4x4 cameraView; //Used to store the current camera view matrix

    //Frame Buffer Objects
    //the first FBO is used to render the shadow-depth pass off-screen to speed the operation up
    //the second FBO is used to render the off-screen the operations needed by the mouse picking function
    //the third FBO is used to render the reflection map
    GLuint mFBO[3];
    GLuint mRenderBuffer[3];

    //options
    bool castShadows; //controls which render mode is currently selected (shadow-mapping or standard).
    bool useFBO; //if GL_EXT_framebuffer_object is not supported, fall back
    bool shootingMode; //activated when clicking on the cue ball, changes the mouse controls and
    //draws the cue. deactivated when the cue is thrown.
    bool physicsDebugModeOn;

    float cueVelocityAfterHit;
    CVector3 cueDirectionAtHit;

    //Overlay information
    string currentScore;
    string activeBalls;
    bool endGame;
    bool gameIsWon;
};

#endif // GLENGINE_H
